/**
 * Created by meiguizhinian on 2018/6/20
 * Copyright © 2018 lixiaolong. All rights reserved.
 */

(function(){
	ivy.InputStream = function (filenameOrData) {
		//Uint8Array
		this._pos = 0;
		if (typeof filenameOrData === "string") {
			this._data = cc.FileUtils.getInstance().getDataFromFile(filenameOrData);
		} else {
			this._data = filenameOrData;
		}
	};
	let p = ivy.InputStream.prototype;
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	p.empty = function () {
		return this._data===null || this._data.length===0;
	};
	p.reset = function () {
		this._pos = 0;
	};
	//-> getDataSize
	p.size = function () {
		if (this._data===null) {
			return 0;
		}
		return this._data.length;
	};
	p.finish = function() {
		return this._pos===this._data.length;
	};
	p.move = function(pos) {
		this._pos += pos;
	};
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	p.readJInt64 = function () {
		return this._decodeJInt(64, true);
	};
	p.readJUInt64 = function() {
		return this._decodeJInt(64, false);
	};

	p.readJInt32  = function () {
		return this._decodeJInt(32, true);
	};
	p.readJUInt32  = function () {
		return this._decodeJInt(32, false);
	};

	p.readJInt16  = function () {
		return this._decodeJInt(16, true);
	};
	p.readJUInt16  = function () {
		return this._decodeJInt(16, false);
	};

	p.readInt64  = function () {
		return this._decodeInt(64, true);
	};
	p.readUInt64  = function() {
		return this._decodeInt(64, false);
	};

	p.readInt32  = function () {
		return this._decodeInt(32, true);
	};
	p.readUInt32  = function () {
		return this._decodeInt(32, false);
	};

	p.readInt16  = function () {
		return this._decodeInt(16, true);
	};
	p.readUInt16  = function () {
		return this._decodeInt(16, false);
	};

	p.readInt8  = function () {
		return this._decodeInt(8, true);
	};
	p.readUInt8  = function () {
		return this._decodeInt(8, false);
	};

	p.readBool  = function () {
		return this.readInt8()===1;
	};

	p.readInt8Array  = function (array, count) {
		for (let i=0; i!==count; ++i) {
			array[i] = this.readInt8();
		}
	};
	p.readUInt8Array  = function (array, count) {
		for (let i=0; i!==count; ++i) {
			array[i] = this.readUInt8();
		}
	};

	p.readStringUTF8  = function (len) {
		if (!len) {
			len = this.readJInt16();
		}
		let needURI = false;
		let bytes = [];
		for (let i=0; i!==len; ++i) {
			let byte = this.readUInt8();
			bytes[i] = byte;
			if (byte>127) {
				//有ascii以外字符
				needURI = true;
			}
		}
		if (needURI) {
			let string = "";
			for (let i=0; i!==len; ++i) {
				string += ('%' + bytes[i].toString(16));
			}
			return decodeURIComponent(string);
		} else {
			return String.fromCharCode.apply(String, bytes);
		}
	};
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////Int

	p._checkSize = function (neededBits) {
		if (!(this._pos + Math.ceil(neededBits / 8) < this._data.length))
			throw new Error("Index out of bound");
	};

	p._decodeFloat = function (precisionBits, exponentBits) {
		let length = precisionBits + exponentBits + 1;
		let size = length >> 3;
		this._checkSize(length);

		let bias = Math.pow(2, exponentBits - 1) - 1;
		let signal = this._readBits(precisionBits + exponentBits, 1, size);
		let exponent = this._readBits(precisionBits, exponentBits, size);
		let significand = 0;
		let divisor = 2;
		let curByte = 0; //length + (-precisionBits >> 3) - 1;
		let startBit = 0;
		do {
			let byteValue = this._readByte(++curByte, size);
			startBit = precisionBits % 8 || 8;
			let mask = 1 << startBit;
			while (mask >>= 1) {
				if (byteValue & mask)
					significand += 1 / divisor;
				divisor *= 2;
			}
		} while (precisionBits -= startBit);

		this._pos += size;

		return exponent === (bias << 1) + 1 ? significand ? NaN : signal ? -Infinity : +Infinity
			: (1 + signal * -2) * (exponent || significand ? !exponent ? Math.pow(2, -bias + 1) * significand
			: Math.pow(2, exponent - bias) * (1 + significand) : 0);
	};

	p._readByte = function (i, size) {
		return this._data[this._pos + size - i - 1];
	};

	p._decodeInt = function (bits, signed) {
		let x = this._readBits(0, bits, bits / 8), max = Math.pow(2, bits);
		let result = signed && x >= max / 2 ? x - max : x;

		this._pos += bits / 8;
		return result;
	};

	p._shl = function (a, b) {
		for (++b; --b; a = ((a %= 0x7fffffff + 1) & 0x40000000) === 0x40000000 ? a * 2 : (a - 0x40000000) * 2 + 0x7fffffff + 1) {
		}
		return a;
	};

	p._readBits = function (start, length, size) {
		let offsetLeft = (start + length) % 8;
		let offsetRight = start % 8;
		let curByte = size - (start >> 3) - 1;
		let lastByte = size + (-(start + length) >> 3);
		let diff = curByte - lastByte;

		//从前往后读，后读取的作为高位//

		let sum = (this._readByte(curByte, size) >> offsetRight) & ((1 << (diff ? 8 - offsetRight : length)) - 1);

		if (diff && offsetLeft)
			sum += (this._readByte(lastByte++, size) & ((1 << offsetLeft) - 1)) << (diff-- << 3) - offsetRight;

		while (diff)
			sum += this._shl(this._readByte(lastByte++, size), (diff-- << 3) - offsetRight);

		return sum;
	};

	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////JInt

	p._decodeJInt = function (bits, signed) {
		let x = this._readJBits(0, bits, bits / 8), max = Math.pow(2, bits);
		let result = signed && x >= max / 2 ? x - max : x;

		this._pos += bits / 8;
		return result;
	};

	p._readJBits= function (start, length, size) {
		let offsetLeft = (start + length) % 8;
		let offsetRight = start % 8;
		let curByte = size - (start >> 3) - 1;
		let lastByte = size + (-(start + length) >> 3);
		let diff = curByte - lastByte;

		//从后往前读，后读取的作为高位//

		let sum = (this._readByte(lastByte, size) >> offsetRight) & ((1 << (diff ? 8 - offsetRight : length)) - 1);

		if (diff && offsetLeft)
			sum += (this._readByte(curByte--, size) & ((1 << offsetLeft) - 1)) << (diff-- << 3) - offsetRight;

		while (diff)
			sum += this._shl(this._readByte(curByte--, size), (diff-- << 3) - offsetRight);

		return sum;
	};
})();
